package com.wuliang.generate.common.sharding.factories;

import com.wuliang.generate.common.sharding.properties.DataSourceConfigProperties;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 预留工厂类。根据配置文件中DataSource 路径，动态创建多数据源
 *
 * @author WuLiang
 */
@Slf4j
@Configuration
@Order(Integer.MIN_VALUE)
@EnableConfigurationProperties(DataSourceConfigProperties.class)
public class AnotherDatasourceConfigFactory {

    @Autowired
    private Environment environment;

    /**
     * 自动注入JPA配置
     */
    @Autowired
    private JpaProperties jpaProperties;

    @Autowired
    private JpaVendorAdapter jpaVendorAdapter;

    /**
     * 自动注入配置文件的上下文
     */
    @Autowired
    private ConfigurableApplicationContext applicationContext;

    /**
     * 自动注入配置文件
     */
    @Autowired
    private DataSourceConfigProperties dataSourceConfigProperties;

    /**
     * bean定义注册器。
     */
    private BeanDefinitionRegistry beanDefinitionRegistry;

    private JpaRepositoryConfigExtension extension = new JpaRepositoryConfigExtension();

    private final Map<String, Class<?>> jpaConfigNameList = new LinkedHashMap<>();

    private static final String REPOSITORY_PACKAGE_PATH = "com.wuliang.generate.repositories.";

    /**
     * 根据配置文件初始化数据源
     */
    @Bean
    @ConditionalOnMissingBean(DataSource.class)
    public void initConfigProperties() {
        // 初始化bean注册工厂
        beanDefinitionRegistry = ((BeanDefinitionRegistry) applicationContext.getBeanFactory());
        // 在指定项目中要开启哪个数据源。
        String enableWhich = dataSourceConfigProperties.getEnableWhich();
        List<String> enableWhichList = new ArrayList<>();
        // 按逗号切开，并转存为  数组
        if (enableWhich.contains(",")) {
            enableWhichList.addAll(Arrays.stream(enableWhich.trim().split(",")).collect(Collectors.toList()));
        } else {
            // 如果没有进行配置，则取默认的system数据源
            enableWhichList.add(enableWhich);
        }
/*
        // 通过反射获取有哪些数据源配置
        ClassUtils.getClassAllField(dataSourceConfigProperties.getClass(), dataSourceConfigProperties.getClass())
                  .stream()
                  // 如果要开启的数据源中不包括当前遍历的数据源配置，则直接返回。
                  .filter(fieldName -> !fieldName.equals("enableWhich") || enableWhichList.contains(fieldName))
                  // 遍历要使用的数据源配置
                  .forEach(datasourceName -> {
                      // 调用对应的 Getter 方法来获取数据源配置对象
                      DataSourceProperties anotherDataSourceProperties =
                              (DataSourceProperties) ClassUtils
                                      .invokeGetterMethod(dataSourceConfigProperties, datasourceName);
                      // 如果数据源配置不为空，则继续
                      if (anotherDataSourceProperties != null) {
                          // 开始装配 Druid 类型的数据源
                          log.info("The datasource will initial [{}]", datasourceName);
                          DruidDataSource druidDataSource = new DruidDataSource();
                          druidDataSource.setUrl(anotherDataSourceProperties.getUrl());
                          druidDataSource.setUsername(anotherDataSourceProperties.getUsername());
                          druidDataSource.setPassword(anotherDataSourceProperties.getPassword());
                          druidDataSource.setDriverClassName(anotherDataSourceProperties.getDriverClassName());
                          // 根据数据源以及数据源名称装配 数据源的Bean
                          registryBean(datasourceName, druidDataSource);
                      }
                  });
*/
    }

    /**
     * 注册数据源Bean
     *
     * @param dataSourceName 数据源名称
     * @param dataSource     数据源配置
     */
    private void registryBean(String dataSourceName, DataSource dataSource) {
        // 注入实体类管理工厂建造者
        EntityManagerFactoryBuilder factoryBuilder = registryEntityManagerFactoryBuilderBean(dataSourceName);
        // 根据实体类管理工厂建造者来注入实体类管理工厂
        LocalContainerEntityManagerFactoryBean factoryBean =
                registryEntityManagerFactoryBean(dataSourceName, dataSource, factoryBuilder);
        // 获取实体类管理工厂
        EntityManagerFactory factory = factoryBean.getObject();
        // 如果工厂对象不为空，则继续下列注入步骤
        if (factory != null) {
            // 注入数据源
            registerBeanByDefinition(dataSourceName, dataSource);
            // 注入实体类管理器
            registryEntityManagerBean(dataSourceName, factory);
            // 注入对应的事务管理器
            registryTransactionManagerBean(dataSourceName, factory);
            // 根据数据名动态加载指定包下的Mapper类
            assemblyJpaConfig(dataSourceName);

        }
    }

    /**
     * 实例化定义bean的建造者
     *
     * @param beanObject 被定义的bean的对象
     * @return 定义bean的建造者
     */
    private BeanDefinitionBuilder genericBeanDefinition(Object beanObject) {
        return BeanDefinitionBuilder.genericBeanDefinition(beanObject.getClass());
    }

    /**
     * 注入bean
     *
     * @param beanName bean 别名
     * @param bean     要注入的对象
     */
    private void registerBeanByDefinition(String beanName, Object bean) {
        beanDefinitionRegistry.registerBeanDefinition(beanName, genericBeanDefinition(bean).getRawBeanDefinition());
    }

    /**
     * 注入管理器工厂
     *
     * @param dataSourceName              数据源别名
     * @param dataSource                  数据源配置
     * @param entityManagerFactoryBuilder 工厂建造器
     * @return 管理器工厂Bean
     */
    private LocalContainerEntityManagerFactoryBean registryEntityManagerFactoryBean(String dataSourceName,
                                                                                    DataSource dataSource,
                                                                                    EntityManagerFactoryBuilder entityManagerFactoryBuilder) {
        // 根据 数据源，JPA的相关配置，以及数据源别名来创建工厂对象
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
                entityManagerFactoryBuilder.dataSource(dataSource)
                        .properties(jpaProperties.getProperties())
                        .packages("com.wuliang.generate.bean.entities." + dataSourceName)
                        .persistenceUnit(dataSourceName + "PersistenceUnit")
                        .build();
        // 注入bean
        registerBeanByDefinition(dataSourceName + "EntityManagerFactoryBean", entityManagerFactoryBean);
        // 并调用bean的初始化
        entityManagerFactoryBean.afterPropertiesSet();
        // 返回工厂对象
        return entityManagerFactoryBean;
    }

    /**
     * 注入管理器工厂的建造者对象
     *
     * @param dataSourceName 数据源别名
     * @return 管理器工厂的建造者对象
     */
    private EntityManagerFactoryBuilder registryEntityManagerFactoryBuilderBean(String dataSourceName) {
        // 根据JPA的相关配置来创建工厂建造者
        EntityManagerFactoryBuilder factoryBuilder =
                new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
        // 注入bean
        registerBeanByDefinition(dataSourceName + "EntityManagerFactoryBuilder", factoryBuilder);
        // 返回工厂建造者对象
        return factoryBuilder;
    }

    /**
     * 注入事务管理管理器
     *
     * @param dataSourceName 数据源别名
     * @param factory        数据源工厂
     */
    private void registryEntityManagerBean(String dataSourceName,
                                           EntityManagerFactory factory) {
        // 用数据源工厂来创建一个具体的实体类管理器
        EntityManager entityManager = factory.createEntityManager();
        // 注入bean
        registerBeanByDefinition(dataSourceName + "EntityManager", entityManager);
    }

    /**
     * 注入事务管理器
     *
     * @param dataSourceName 数据源别名
     * @param factory        数据源工厂
     */
    private void registryTransactionManagerBean(String dataSourceName,
                                                EntityManagerFactory factory) {
        // 创建一个JPA的事务管理器
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        // 将实体类管理器的工厂对象赋值进去
        transactionManager.setEntityManagerFactory(factory);
        // 注入bean
        registerBeanByDefinition(dataSourceName + "TransactionManager", transactionManager);
    }

    private AnnotationRepositoryConfigurationSource assemblyConfigurationSource() {
//        AnnotationMetadata.introspect();
//        return new AnnotationRepositoryConfigurationSource();
        return null;
    }

    private void scannerRepositoryPackages(String dataSourceName) {
        String repositoryPackage = REPOSITORY_PACKAGE_PATH + dataSourceName;

    }

    private void assemblyJpaConfig(String dataSourceName) {
        String jpaConfigPath = "com.wuliang.generate.common.core.configs.datasource";
        Reflections reflections = new Reflections(jpaConfigPath, new SubTypesScanner());
        System.out.println(reflections.getAllTypes());
    }

    private void createAnnotationMetadata() {

    }

}
